package avl.sv.shared.model.featureGenerator;

import ij.ImagePlus;
import ij.Macro;
import ij.measure.Measurements;
import ij.measure.ResultsTable;
import ij.plugin.filter.Analyzer;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.JPanel;

public class FeatureGeneratorImageJ extends AbstractFeatureGenerator implements Serializable {

    public FeatureGeneratorImageJ() {
        measurements = Measurements.SCIENTIFIC_NOTATION;
    }

    private int measurements = Measurements.MEAN | Measurements.STD_DEV | Measurements.MODE | Measurements.MIN_MAX | Measurements.MEDIAN | Measurements.SKEWNESS | Measurements.KURTOSIS | Measurements.SCIENTIFIC_NOTATION;
    private ArrayList<String> featureNames = new ArrayList<String>();

    @Override
    public void setFeatureNames(String[] names) {
        featureNames = new ArrayList<>();
        for (String name:names){
            featureNames.add(name);
        }
        measurements = Measurements.SCIENTIFIC_NOTATION;
        for (String featureName:featureNames){
            switch (featureName){
                case "Mean":
                    measurements |= Measurements.MEAN;
                    break;
                case "StdDev":
                    measurements |= Measurements.STD_DEV;
                    break;
                case "Mode":
                    measurements |= Measurements.MODE;
                    break;
                case "Min":
                    measurements |= Measurements.MIN_MAX;
                    break;
                case "Max":
                    measurements |= Measurements.MIN_MAX;
                    break;
                case "Median":
                    measurements |= Measurements.MEDIAN;
                    break;
                case "Skew":
                    measurements |= Measurements.SKEWNESS;
                    break;
                case "Kurt":
                    measurements |= Measurements.KURTOSIS;
                    break;                  
            }
        }
    }
    
    public int getMeasurements() {
        return measurements;
    }

    @Override
    public double[][] getFeaturesForImages(BufferedImage imgs[]) {
        double features[][] = new double[imgs.length][];
        for (int i = 0; i < imgs.length; i++) {
            ResultsTable rt = generateResultsTable(imgs[i]);
            double[] featuresTemp = getFeaturesFromTable(rt);
            features[i] = featuresTemp;
        }
        return features;
    }

    @Override
    public String[] getFeatureNames(){
        return featureNames.toArray(new String[featureNames.size()]);
    }

    @Override
    public int getNumberOfFeatures() {
        return featureNames.size();
    }

    public double[] getFeaturesFromTable(ResultsTable rt) {
        int fSize = featureNames.size();
        double[] features = new double[fSize];
        for (int i = 0; (i < featureNames.size()); i++) {
            double[] data = rt.getColumnAsDoubles(rt.getColumnIndex(featureNames.get(i)));
            if (data == null) {
                continue;
            }
            features[i] = data[0];
        }
        return features;
    }

    public ResultsTable generateResultsTable(BufferedImage img) {
        Thread thread = Thread.currentThread();
        String name = thread.getName();
        if (!name.startsWith("Run$_")) {
            thread.setName("Run$_" + name);
        }
        Macro.setOptions("dummy");
        ResultsTable rt = new ResultsTable();
        ImagePlus impTemp = new ImagePlus("", img);
        Analyzer.setMeasurements(measurements);
        Analyzer.setResultsTable(rt);
        Analyzer a = new Analyzer(impTemp, measurements, rt);
        a.measure();
        return rt;
    }

    @Override
    public String getName() {
        return "FeaturesImageJ";
    }

    private class OptionsPromptImageJFeatures extends javax.swing.JPanel implements FeatureGeneratorOptionsPromptInterface {

        private javax.swing.JCheckBox jCheckBoxGrayMean = new javax.swing.JCheckBox();
        private javax.swing.JCheckBox jCheckBoxGrayMinMax = new javax.swing.JCheckBox();
        private javax.swing.JCheckBox jCheckBoxGrayModal = new javax.swing.JCheckBox();
        private javax.swing.JCheckBox jCheckBoxKurtosis = new javax.swing.JCheckBox();
        private javax.swing.JCheckBox jCheckBoxMedian = new javax.swing.JCheckBox();
        private javax.swing.JCheckBox jCheckBoxSkewness = new javax.swing.JCheckBox();
        private javax.swing.JCheckBox jCheckBoxStd = new javax.swing.JCheckBox();
        private javax.swing.JPanel jPanel1 = new javax.swing.JPanel();

        private void updateMeasurements() {
            ArrayList<String> tempFeature = new ArrayList<>();            
            if (jCheckBoxStd.isSelected()) {
                tempFeature.add("StdDev");
            }
            if (jCheckBoxGrayMinMax.isSelected()) {
                tempFeature.add("Min");
                tempFeature.add("Max");
            }
            if (jCheckBoxSkewness.isSelected()) {
                tempFeature.add("Skew");
            }
            if (jCheckBoxGrayMean.isSelected()) {
                tempFeature.add("Mean");
            }
            if (jCheckBoxGrayModal.isSelected()) {
                tempFeature.add("Mode");
            }
            if (jCheckBoxMedian.isSelected()) {
                tempFeature.add("Median");
            }
            if (jCheckBoxKurtosis.isSelected()) {
                tempFeature.add("Kurt");
            }
            setFeatureNames(tempFeature.toArray(new String[tempFeature.size()]));
        }

        @Override
        public AbstractFeatureGenerator getFeatureGenerator() {
            FeatureGeneratorImageJ gen = new FeatureGeneratorImageJ();
            gen.setFeatureNames(featureNames.toArray(new String[featureNames.size()]));
            return gen;
        }

        public OptionsPromptImageJFeatures(FeatureGeneratorImageJ featureGeneratorImageJ) {
            
            ActionListener updateAction = new ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    updateMeasurements();
                }
            };

            jCheckBoxKurtosis.setText("Kurtosis");
            jCheckBoxKurtosis.addActionListener(updateAction);

            jCheckBoxMedian.setText("Median");
            jCheckBoxMedian.addActionListener(updateAction);

            jCheckBoxGrayModal.setText("Gray modal");
            jCheckBoxGrayModal.addActionListener(updateAction);

            jCheckBoxGrayMean.setText("Gray mean");
            jCheckBoxGrayMean.addActionListener(updateAction);

            jCheckBoxSkewness.setText("Skewness");
            jCheckBoxSkewness.addActionListener(updateAction);

            jCheckBoxGrayMinMax.setText("Gray min max value");
            jCheckBoxGrayMinMax.addActionListener(updateAction);

            jCheckBoxStd.setText("Standard deviation");
            jCheckBoxStd.addActionListener(updateAction);

            javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
            jPanel1.setLayout(jPanel1Layout);
            jPanel1Layout.setHorizontalGroup(
                    jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                            .addContainerGap()
                            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jCheckBoxGrayModal)
                                    .addComponent(jCheckBoxMedian)
                                    .addComponent(jCheckBoxKurtosis)
                                    .addComponent(jCheckBoxStd)
                                    .addComponent(jCheckBoxGrayMinMax)
                                    .addComponent(jCheckBoxSkewness)
                                    .addComponent(jCheckBoxGrayMean))
                            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );
            jPanel1Layout.setVerticalGroup(
                    jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                            .addContainerGap()
                            .addComponent(jCheckBoxStd)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jCheckBoxGrayMinMax)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jCheckBoxSkewness)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jCheckBoxGrayMean)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jCheckBoxGrayModal)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jCheckBoxMedian)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jCheckBoxKurtosis)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
            this.setLayout(layout);
            layout.setHorizontalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            );
            layout.setVerticalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            );
            
            for (String featureName : featureNames) {
                switch (featureName) {
                    case "Mean":
                        jCheckBoxGrayMean.setSelected(true);
                        break;
                    case "StdDev":
                        jCheckBoxStd.setSelected(true);
                        break;
                    case "Mode":
                        jCheckBoxGrayModal.setSelected(true);
                        break;
                    case "Min":
                        jCheckBoxGrayMinMax.setSelected(true);
                        break;
                    case "Max":
                        jCheckBoxGrayMinMax.setSelected(true);
                        break;
                    case "Median":
                        jCheckBoxMedian.setSelected(true);
                        break;
                    case "Skew":
                        jCheckBoxSkewness.setSelected(true);
                        break;
                    case "Kurt":
                        jCheckBoxKurtosis.setSelected(true);
                        break;
                }
            }
            updateMeasurements();
        }        
    }

    @Override
    public JPanel getOptionsPanel() {
        return new OptionsPromptImageJFeatures(this);
    }

}
